#include "Sim900.h"
#include "Assert.h"

module Sim900TcpActiveMessageP
{
	provides interface SplitControl;
	provides interface AMSend[uint8_t amid];
	provides interface Receive[uint8_t amid];
	provides interface Init;
	
	uses interface AMPacket;
	uses interface Packet;
	uses interface SplitControl as Sim900Control;
	uses interface Sim900DriverLayer;
	uses interface Leds;
	uses interface Timer<TMilli> as Timer;
#ifdef SIM900_AM_DEBUG
	uses interface DiagMsg;
#endif
}
implementation
{	
	
	norace message_t *txMsg;
	norace message_t *lastMsg;
	norace message_t *rxMsg;
	norace message_t *rxFirstMsg;
	norace error_t sndErr;
	norace error_t spltErr;
	
	uint16_t *msgCrc;
	char *frameEnd;
	void *msgPload;
	void *txBuf = NULL;
	void *rxBuf = NULL;
	sim900_packet_t *pPacket;
	uint8_t fullLength;
	uint8_t i;
	
	norace uint8_t sendRetries = 0;
	norace uint8_t startRetries = 0;
	norace bool busy = FALSE;
	norace uint8_t state;
	
	task void socketConnect();
	task void createPacket();
	task void sendPacket();
	task void receivePacket();
	task void signalSendDone();
	task void signalStartDone();
	
	
#ifdef SIM900_AM_DEBUG

	void DiagStr(unsigned char *str)
	{
		if(call DiagMsg.record())
		{
			call DiagMsg.str(str);
			call DiagMsg.send();
		}
	}

	void DiagStrNum(unsigned char *str, uint8_t num)
	{
		if(call DiagMsg.record())
		{
			call DiagMsg.str(str);
			call DiagMsg.uint8(num);
			call DiagMsg.send();
		}
	}

#endif

	event void Timer.fired()
	{
		
	}
	command error_t Init.init()
	{
		state = AMSTATE_OFF;
		txMsg = NULL;
		sndErr = EOFF;
		
		return SUCCESS;
	}
	
	command error_t SplitControl.start()
	{
		error_t err = EINVAL;
		
		startRetries = 0;
		//The GSM module is OFF, needed to power it on.
		if(state == AMSTATE_OFF) 
		{
			err = call Sim900Control.start();
			if(err == SUCCESS) call Leds.led0On();
			
			#ifdef SIM900_AM_DEBUG
			DiagStr("C-START0-PW");
			#endif
			
			return SUCCESS;
		}
		//The GSM module has been already started, just start the socket
		else if(state == AMSTATE_STARTING) 
		{
			post socketConnect();
			
			return SUCCESS;
		}
		else return EALREADY;
	}
	
	event void Sim900Control.startDone(error_t error)
	{
		#ifdef SIM900_AM_DEBUG
		DiagStrNum("D-START0-PW", error);
		#endif
		
		if(error == SUCCESS)
		{
			state = AMSTATE_STARTING;
			call Leds.led0Off();
			
			post socketConnect();
		}
		else
		{
			spltErr = error;
			post signalStartDone();
		}
	}
	
	
	
	command error_t SplitControl.stop()
	{
		error_t err;
		
		if(state != AMSTATE_ON)
		{
			err = call Sim900DriverLayer.closeSocket();
			if(err == SUCCESS) 
			{
				
				#ifdef SIM900_AM_DEBUG
				DiagStr("C-STOP0-SO");
				#endif
				
				call Leds.led0On();
			}
			
			return err;
		}
		else 
		{
			#ifdef SIM900_AM_DEBUG
			DiagStr("C-STOP0-PW");
			#endif
			
			return call Sim900Control.stop();
		}
	}
	
	event void Sim900DriverLayer.closeSocketDone(error_t success)
	{
		if(call Sim900Control.stop() != SUCCESS) signal SplitControl.stopDone(success);
		else
		{
			#ifdef SIM900_AM_DEBUG
			DiagStr("C-STOP1-PW");
			#endif
		}
	}
	
	event void Sim900Control.stopDone(error_t error)
	{
		call Leds.led0Off();
	
		state = AMSTATE_OFF;
		if(txBuf != NULL) free(txBuf);
		txBuf = NULL;
		txMsg = NULL;
		
		#ifdef SIM900_AM_DEBUG
		DiagStr("D-STOP1-PW");
		#endif
		
		signal SplitControl.stopDone(error);
	}

	command error_t AMSend.send[uint8_t amid](am_addr_t addr, message_t *msg, uint8_t len)
	{
		if(state == AMSTATE_SENDING || txMsg != NULL) return EBUSY;
		else if(len > call Packet.maxPayloadLength()) return ESIZE;
		else
		{
			sendRetries = 0;
			
			call AMPacket.setDestination(msg, addr);
			call AMPacket.setType(msg, amid);
			call Packet.setPayloadLength(msg, len);
			
			txMsg = msg;
				
			state = AMSTATE_SENDING;
			post createPacket();
			
			#ifdef SIM900_AM_DEBUG
			DiagStr("C-SEND");
			#endif
			
			return SUCCESS;
		}
	}
	

	command void * AMSend.getPayload[uint8_t amid](message_t *msg, uint8_t len){
		return call Packet.getPayload(msg, len);
	}

	command uint8_t AMSend.maxPayloadLength[uint8_t amid]()
	{
		return call Packet.maxPayloadLength();
	}

	command error_t AMSend.cancel[uint8_t amid](message_t *msg)
	{
		return FAIL;
	}

	event void Sim900DriverLayer.cmgsDone(error_t success){
		// TODO Auto-generated method stub
	}

	event void Sim900DriverLayer.resetSocketDone(error_t success){
		// TODO Auto-generated method stub
	}

	event void Sim900DriverLayer.socketDisposed()
	{
		#ifdef SIM900_AM_DEBUG
		DiagStr("EV-SOCK-DISP");
		#endif
	}
	
	task void createPacket()
	{
		/*
		 * TODO: There are some garbage bytes, they need to be removed
		 * - Dispatch byte is part of serial header,
		 * here we don't need that, only for compatibility of serial messages
		 * at the PC side.
		 * - Two empty bytes of CRC, they are here for the same reason, compatibility,
		 * TCP does the CRC at underlying layers.
		 */
		uint8_t txPloadLen = call Packet.payloadLength(txMsg);
		
		call Leds.led0On();
		#ifdef SIM900_AM_DEBUG
		DiagStr("T-PACK");
		#endif
		
		ASSERT(txMsg != NULL);
		
		fullLength = (nx_uint8_t)(sizeof(sim900_packet_t) + txPloadLen + 4);
		//fullLength = 10;
		msgPload = call Packet.getPayload(txMsg, call Packet.payloadLength(txMsg));
		 
		if (txBuf == NULL)
		{
			txBuf = malloc(fullLength);
		}
		else
		{
			txBuf = realloc(txBuf, fullLength);
		}
		
		ASSERT(txBuf != NULL);
			
		*((uint8_t *)txBuf) = 0x7E;
		pPacket = (sim900_packet_t *)(txBuf + 1);
		pPacket->header.fullLenght = fullLength;
				
				
		pPacket->header.protocol = 0x45;
		pPacket->header.dispatch = 0x00;
		pPacket->header.dest = call AMPacket.destination(txMsg);
		pPacket->header.src = call AMPacket.source(txMsg);
		pPacket->header.length = txPloadLen;
		pPacket->header.group = (nx_am_group_t)call AMPacket.group(txMsg);
		pPacket->header.type = (nx_am_id_t)call AMPacket.type(txMsg);
				
		memcpy(pPacket->data, msgPload, txPloadLen);
				
		msgCrc = (uint16_t *)(&pPacket->data[txPloadLen]);
		*(msgCrc) = 0;
		frameEnd = (char *)(&pPacket->data[txPloadLen + 2]);
		*(frameEnd) = 0x7E;	
		
		
		/* for(i = 0; i < 10; i++)
		{
			*((uint8_t *)(txBuf + i)) = i;
		} */
		
		post sendPacket();
		call Leds.led0Off();
	}
	
	task void sendPacket()
	{
		ASSERT(txBuf != NULL);
		ASSERT(state == AMSTATE_SENDING);
		
		sndErr = call Sim900DriverLayer.writeSocket(txBuf, fullLength);
		
		if(sndErr != SUCCESS)
		{
			call Leds.led2Off();
			
			#ifdef SIM900_AM_DEBUG
			DiagStrNum("T-SEND-REOPEN:", sendRetries);
			#endif
			
			post socketConnect();
		}
		else 
		{
			#ifdef SIM900_AM_DEBUG
			DiagStrNum("T-SEND:", sendRetries);
			#endif
			
			call Leds.led2On();
		}
	}
	
	event void Sim900DriverLayer.writeSocketDone(error_t success)
	{
		#ifdef SIM900_AM_DEBUG
		DiagStrNum("D-SEND", success);
		#endif
		
		if(success == SUCCESS)
		{
			#ifdef SIM900_AM_DEBUG
			DiagStr("D-SEND-OK");
			#endif
			
			post signalSendDone();
		}
		else if(sendRetries < SIM900_RESEND)
		{
			sendRetries++;
			
			#ifdef SIM900_AM_DEBUG
			DiagStr("D-SEND-RESEND");
			#endif
			
			post sendPacket();
		}
		else
		{
			
			#ifdef SIM900_AM_DEBUG
			DiagStr("D-SEND-RECONN");
			#endif
			
			post socketConnect();
		}
	}
	
	task void socketConnect()
	{
		error_t err = call Sim900DriverLayer.openSocket();
		
		if(err != SUCCESS)
		{
			if(state == AMSTATE_STARTING)
			{
				spltErr = err;
				post signalStartDone();
			}
			else if(state == AMSTATE_SENDING)
			{
				sndErr = err;
				post signalSendDone();
			}
			else
			{
				#ifdef SIM900_AM_DEBUG
				DiagStrNum("ERR-T-SOCK-INV", state);
				#endif
			}
		}
		else
		{
			call Leds.led1On();
			
			#ifdef SIM900_AM_DEBUG
			DiagStrNum("T-SOCKET-CONN", startRetries);
			#endif
		}
	}
	
	event void Sim900DriverLayer.openSocketDone(error_t success)
	{
		call Leds.led1Off();
		#ifdef SIM900_AM_DEBUG
		DiagStrNum("D-SOCKET-CONN", success);
		#endif
		
		if(success == SUCCESS)
		{
			if(state == AMSTATE_STARTING)
			{
				state = AMSTATE_ON;
				
				spltErr = success;
				startRetries = 0;
				
				#ifdef SIM900_AM_DEBUG
				DiagStrNum("D-SOCK-C-ST", success);
				#endif
				
				post signalStartDone();
			}
			else if(state == AMSTATE_SENDING)
			{
				#ifdef SIM900_AM_DEBUG
				DiagStrNum("D-SOCK-C-SND", success);
				#endif
				
				startRetries = 0;
				post sendPacket();
			}
			else
			{
				#ifdef SIM900_AM_DEBUG
				DiagStrNum("ERR-D-SOCK1", state);
				#endif
			}
		}
		else if(startRetries < SIM900_RECONNECT)
		{
			startRetries++;
			post socketConnect();
		}
		else
		{
			startRetries = 0;
			
			if(state == AMSTATE_STARTING)
			{
				spltErr = success;
				post signalStartDone();
			}
			else if(state == AMSTATE_SENDING)
			{
				sndErr == success;
				post signalSendDone();
			}
			else
			{
				#ifdef SIM900_AM_DEBUG
				DiagStrNum("ERR-D-SOCK2", state);
				#endif
			}
		}
	}
	
	event void Sim900DriverLayer.socketDataReceived(char *data)
	{
		uint8_t len;
		sim900_packet_t *pRcvdPacket;
		void *pLoad;
		
		pRcvdPacket = (sim900_packet_t *)((uint8_t *)data + 1);
		pLoad = call Packet.getPayload(rxMsg, pRcvdPacket->header.length);
		
		if(rxMsg == NULL)
		{
			rxFirstMsg = malloc(sizeof(message_t));
			rxMsg = rxFirstMsg;
		}	
		
		ASSERT(rxMsg != NULL);
		
		call Packet.clear(rxMsg);
		
		call AMPacket.setDestination(rxMsg, pRcvdPacket->header.dest);
		call AMPacket.setSource(rxMsg, pRcvdPacket->header.src);
		call AMPacket.setGroup(rxMsg, pRcvdPacket->header.group);
		call AMPacket.setType(rxMsg, pRcvdPacket->header.type);
		
		memcpy(pLoad, (void *)pRcvdPacket->data[0], pRcvdPacket->header.length);
		
		call Packet.setPayloadLength(rxMsg, pRcvdPacket->header.length);
		
		#ifdef SIM900_AM_DEBUG
		DiagStrNum("E-RCV", pRcvdPacket->header.length);
		
		/* if(call DiagMsg.record())
		{
			call DiagMsg.uint16(pRcvdPacket->header.src);
			call DiagMsg.uint16(pRcvdPacket->header.dest);
			call DiagMsg.uint8(pRcvdPacket->header.length);
			call DiagMsg.uint8(pRcvdPacket->data[0]); 
			call DiagMsg.send();
		} */
		#endif
		
		post receivePacket();
	}
	
	task void receivePacket()
	{
		am_id_t amid = call AMPacket.type(rxMsg);
		uint8_t len = call Packet.payloadLength(rxMsg);
		void * pLoad = call Packet.getPayload(rxMsg, len);
		message_t *newMsg;
		
		#ifdef SIM900_AM_DEBUG
		DiagStr("T-RCV");
		
		if(call DiagMsg.record())
		{
			call DiagMsg.str("amid");
			call DiagMsg.uint8(amid);
			call DiagMsg.str("len");
			call DiagMsg.uint8(len); 
			call DiagMsg.send();
		}
		#endif
		
		newMsg = signal Receive.receive[amid](rxMsg, pLoad, len);
		
		if(newMsg != rxFirstMsg) 
		{
			free(rxFirstMsg);
			rxMsg = newMsg;
			
			ASSERT(rxFirstMsg == NULL); 
			
			#ifdef SIM900_AM_DEBUG
			DiagStr("T-RCV-NEW-BUF");
			#endif
		}
	}
	
	task void signalSendDone()
	{
		call Leds.led2Off();
		
		state = AMSTATE_ON;
		sendRetries = 0;
		
		lastMsg = txMsg;
		txMsg = NULL;
		
		#ifdef SIM900_AM_DEBUG
		DiagStrNum("SGN-SEND", sndErr);
		#endif
		
		signal AMSend.sendDone[call AMPacket.type(lastMsg)](lastMsg, sndErr);
	}
	
	task void signalStartDone()
	{
		
		#ifdef SIM900_AM_DEBUG
		DiagStrNum("SGN-START", spltErr);
		#endif
		
		signal SplitControl.startDone(spltErr);
	}
	
	default event void AMSend.sendDone[am_id_t id](message_t *msg, error_t error)
	{
	}
	
	default event message_t * Receive.receive[am_id_t id](message_t* msg, void* payload, uint8_t len)
	{	
	
		#ifdef SIM900_AM_DEBUG
		DiagStr("E-RCV-DEF");
		
		if(call DiagMsg.record())
		{
			call DiagMsg.str("amid");
			call DiagMsg.uint8(id);
			call DiagMsg.str("len");
			call DiagMsg.uint8(len); 
			call DiagMsg.send();
		}
		#endif
		return msg;
	}
}